package com.mossle.simulator.restful; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import com.mossle.core.mapper.JsonMapper; import com.mossle.core.spring.ApplicationContextHelper; import com.mossle.core.spring.DateConverter; import com.mossle.core.util.IoUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; public class RestFilter implements Filter { private static Logger logger = LoggerFactory.getLogger(RestFilter.class); private JsonMapper jsonMapper = new JsonMapper(); private DateConverter dateConverter = new DateConverter(); public void init(FilterConfig filterConfig) throws ServletException { } public void destroy() { } public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; ApplicationContext ctx = ApplicationContextHelper .getApplicationContext(); Map<String, Object> map = ctx.getBeansWithAnnotation(Path.class); for (Object object : map.values()) { Class clz = object.getClass(); for (Method method : clz.getDeclaredMethods()) { if (this.matches(request, method)) { Map<String, String> parameters = parseBody(request .getInputStream()); this.invokeMethod(request, response, object, method, parameters); return; } } } filterChain.doFilter(req, res); } public Map<String, String> parseBody(InputStream inputStream) throws IOException { String body = IoUtils.readString(inputStream); // body = URLDecoder.decode(body, "UTF-8"); logger.debug("body : {}", body); Map<String, String> parameters = new HashMap<String, String>(); for (String text : body.split("&")) { logger.debug("text : {}", text); if (text.indexOf("=") == -1) { continue; } int index = text.indexOf("="); String key = text.substring(0, index); String value = text.substring(index + 1); // if (value != null) { // value = URLDecoder.decode(value, "UTF-8"); // } parameters.put(key, value); } return parameters; } public Map<String, String> getMetaData(Annotation[] annotations) { Map<String, String> metaData = new HashMap<String, String>(); for (Annotation annotation : annotations) { String name = null; String type = null; String value = null; String defaultValue = null; if (annotation instanceof PathParam) { name = ((PathParam) annotation).value(); type = "path"; metaData.put("name", name); metaData.put("type", type); // value = this.getPathParam(request, name, method); } else if (annotation instanceof QueryParam) { name = ((QueryParam) annotation).value(); type = "query"; metaData.put("name", name); metaData.put("type", type); // value = request.getParameter(name); } else if (annotation instanceof FormParam) { name = ((FormParam) annotation).value(); type = "form"; metaData.put("name", name); metaData.put("type", type); // value = parameters.get(name); } else if (annotation instanceof HeaderParam) { name = ((HeaderParam) annotation).value(); type = "header"; metaData.put("name", name); metaData.put("type", type); // value = request.getHeader(name); } else if (annotation instanceof DefaultValue) { defaultValue = ((DefaultValue) annotation).value(); metaData.put("defaultValue", defaultValue); } } return metaData; } public void invokeMethod(HttpServletRequest request, HttpServletResponse response, Object object, Method method, Map<String, String> parameters) { try { List arguments = new ArrayList(); Annotation[][] annotationArray = method.getParameterAnnotations(); Class[] parameterTypeArray = method.getParameterTypes(); for (int i = 0; i < annotationArray.length; i++) { if (parameterTypeArray[i] .isAssignableFrom(HttpServletRequest.class)) { arguments.add(request); continue; } if (parameterTypeArray[i] .isAssignableFrom(HttpServletResponse.class)) { arguments.add(response); continue; } Annotation[] annotations = annotationArray[i]; Map<String, String> metaData = this.getMetaData(annotations); String name = metaData.get("name"); String value = null; if ("path".equals(metaData.get("type"))) { value = this.getPathParam(request, name, method); } else if ("query".equals(metaData.get("type"))) { value = request.getParameter(name); } else if ("form".equals(metaData.get("type"))) { value = parameters.get(name); } else if ("header".equals(metaData.get("type"))) { value = request.getHeader(name); } if ((value == null) && metaData.containsKey("defaultValue")) { value = metaData.get("defaultValue"); } if (value != null) { if (parameterTypeArray[i] == String.class) { arguments.add(value); } else if ((parameterTypeArray[i] == Boolean.class) || (parameterTypeArray[i] == boolean.class)) { if (value.length() == 0) { arguments.add(false); } else { arguments.add(Boolean.valueOf(value)); } } else if ((parameterTypeArray[i] == Long.class) || (parameterTypeArray[i] == long.class)) { if (value.length() == 0) { arguments.add(0L); } else { arguments.add(Long.parseLong(value)); } } else if ((parameterTypeArray[i] == Integer.class) || (parameterTypeArray[i] == int.class)) { if (value.length() == 0) { arguments.add(0); } else { arguments.add(Integer.parseInt(value)); } } else if (parameterTypeArray[i] == Date.class) { if (value.length() == 0) { arguments.add(null); } else { arguments.add(dateConverter.convert(value)); } } else { throw new IllegalArgumentException("unsupport type : " + parameterTypeArray[i]); } } else { if (parameterTypeArray[i] == boolean.class) { arguments.add(false); } else if (parameterTypeArray[i] == char.class) { arguments.add((char) 0); } else if (parameterTypeArray[i] == byte.class) { arguments.add((byte) 0); } else if (parameterTypeArray[i] == short.class) { arguments.add((short) 0); } else if (parameterTypeArray[i] == int.class) { arguments.add(0); } else if (parameterTypeArray[i] == long.class) { arguments.add(0L); } else if (parameterTypeArray[i] == float.class) { arguments.add(0F); } else if (parameterTypeArray[i] == double.class) { arguments.add(0D); } else if (parameterTypeArray[i] == String.class) { arguments.add(null); } else { String message = "cannot process method argument, index is : " + i + ", name : " + name + ", value is null"; throw new IllegalArgumentException(message); } } } logger.debug("{}, {}, {}", object, method, arguments); Object result = method.invoke(object, arguments.toArray()); if (result == null) { logger.debug("return null"); } else if (result instanceof String) { response.setContentType(MediaType.TEXT_HTML); response.getOutputStream().write( ((String) result).getBytes("UTF-8")); } else if (result instanceof InputStream) { Produces produces = method.getAnnotation(Produces.class); String contentType = MediaType.APPLICATION_OCTET_STREAM; if (produces != null) { String[] values = produces.value(); if ((values != null) && (values.length > 0)) { contentType = values[0]; } } response.setContentType(contentType); IoUtils.copyStream((InputStream) result, response.getOutputStream()); } else { response.setContentType(MediaType.APPLICATION_JSON); response.getOutputStream().write( jsonMapper.toJson(result).getBytes("UTF-8")); } } catch (Exception ex) { logger.error(ex.getMessage(), ex); } } public boolean matches(HttpServletRequest request, Method method) { if ("GET".equalsIgnoreCase(request.getMethod()) && (method.getAnnotation(GET.class) == null)) { return false; } if ("POST".equalsIgnoreCase(request.getMethod()) && (method.getAnnotation(POST.class) == null)) { return false; } if ("PUT".equalsIgnoreCase(request.getMethod()) && (method.getAnnotation(PUT.class) == null)) { return false; } if ("DELETE".equalsIgnoreCase(request.getMethod()) && (method.getAnnotation(DELETE.class) == null)) { return false; } logger.debug("{}", request.getRequestURI()); String prefix = request.getContextPath() + "/rs"; String requestPath = request.getRequestURI().substring(prefix.length()); logger.debug("{}", requestPath); String path = "/"; if (method.getDeclaringClass().getAnnotation(Path.class) != null) { path += method.getDeclaringClass().getAnnotation(Path.class) .value(); } if (method.getAnnotation(Path.class) != null) { path += ("/" + method.getAnnotation(Path.class).value()); } logger.debug("{}, {}", method, path); String[] src = requestPath.split("/"); String[] dest = path.split("/"); if (src.length != dest.length) { return false; } for (int i = 0; i < src.length; i++) { if (!this.matchPath(src[i], dest[i])) { return false; } } return true; } public boolean matchPath(String src, String dest) { if (src.equals(dest)) { return true; } if (dest.startsWith("{") && dest.endsWith("}")) { return true; } return false; } public String getPathParam(HttpServletRequest request, String name, Method method) { String prefix = request.getContextPath() + "/rs"; String requestPath = request.getRequestURI().substring(prefix.length()); String path = "/"; if (method.getDeclaringClass().getAnnotation(Path.class) != null) { path += method.getDeclaringClass().getAnnotation(Path.class) .value(); } if (method.getAnnotation(Path.class) != null) { path += ("/" + method.getAnnotation(Path.class).value()); } String[] src = requestPath.split("/"); String[] dest = path.split("/"); for (int i = 0; i < dest.length; i++) { if (dest[i].equals("{" + name + "}")) { return src[i]; } } return null; } }